package bliVideo

import com.actor.kotlin_test_application.book_kotlincn.Test123

//16-函数头 https://www.bilibili.com/video/BV1wf4y1s7TG?p=16
/**
 * 函数头
 *
 * 可见性修饰符, 默认public      函数名        构造函数参数                 返回类型(Unit=Void, Any=Object)
 * [private|public]  fun  functionName(age: Int, flag: Boolean = false): [Unit|Any] {
 *     //函数体
 * }
 */


//17-函数参数默认值参 https://www.bilibili.com/video/BV1wf4y1s7TG?p=17
private fun doSomething(age: Int, name: String, flag: Boolean = false) {
    //如果不打算传入值参, 可以预先给参数指定默认值
    println("name = $name, age = $age, flag = $flag")
}


//18-具名函数参数 https://www.bilibili.com/video/BV1wf4y1s7TG?p=18
fun main21() {
    doSomething(name = "张三", age = 23)  //指定参数名称
}


//19-Unit函数 https://www.bilibili.com/video/BV1wf4y1s7TG?p=19
/**
 * 不是所有函数都有返回值，Kotlin中没有返回值的函数叫Unit函数，也就是说他们的返回类型是Unit。
 * 在Kotlin之前，函数不返回任何东西用void描述，意思是“没有返回类型，不会带来什么，忽略它”，也就是说如果函数不返回任何东西就忽略类型。
 * 但是，void这种解决方案无法解释现代语言的一个重要特征，泛型。
 */


//20-Nothing类型 https://www.bilibili.com/video/BV1wf4y1s7TG?p=20
/**
 * TODO函数的任务就是抛出异常，就是永远别指望它运行成功，返回Nothing类
 */
fun main22() {
//public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
    val unN: Nothing = TODO("Test")
}


//21-反引号中的函数名 https://www.bilibili.com/video/BV1wf4y1s7TG?p=21
/**
 * Kotlin可以使用空格和特殊字符对函数命名，不过函数名要用一对反引号括起来。
 * 为了支持Kotlin和Java互操作，而Kotlin和Java各自却有着不同的保留关键字，不能作为函数名，使用反引号括住函数名就能避免任何冲突
 */
//fun `**~special function with weird name**`() {
//    println("I am weird.")
//}
//fun main23() {
//    `**~special function with weird name**`()
//    Test123.`is`()
//}


/**
 * 23-什么是匿名函数 https://www.bilibili.com/video/BV1wf4y1s7TG?p=23
 */
fun main24() {
    //定义时不取名字的函数，我们称之为匿名函数，匿名函数通常整体传递给其他函数或者从其他函数返回.
    //匿名函数对Kotlin来说很重要，有了它，我们能够根据需要制定特殊规则，轻松定制标准库里的内置函数.
    val str = "Mississippi"
    val count = str.count()
    val countM = str.count({ it == 'M' })
    val countI = str.count(predicate = { it == 'i' })
    val countS = str.count({ letter -> letter == 's' })

    val countP = str.count { it == 'p' }
    println("count = $count, countM = $countM, countI = $countI, countS = $countS, countP = $countP")
}


/**
 * 24-函数类型与隐式返回 https://www.bilibili.com/video/BV1wf4y1s7TG?p=24
 */
fun main25() {
    /**
     * 1.匿名函数也有类型，匿名函数可以当作变量赋值给函数类型变量，就像其他变量一样，匿名函数就可以在代码里传递了。
     *   变量有类型，变量可以等于函数，函数也会有类型。
     * 2.函数的类型，由传入的参数和返回值类型决定.
     * 3.和具名函数不一样，除了极少数情况外，匿名函数不需要return关键字来返回数据,
     *   匿名函数会隐式或自动返回函数体"最后一行"语句的结果.
     */
    val bf1:() -> String = { "aa" }   //变量类型=匿名函数,  无参,  返回String
    val bf2:() -> String = {
        val str = "Mississippi"
        val bb = "Happy $str"
        bb
    }
    println("bf1 = $bf1, bf2 = $bf2")           //kotlin.String, kotlin.String
    println("bf1 = ${bf1()}, bf2 = ${bf2()}")   //aa           , Happy Mississippi


    /**
     * 25-函数参数 https://www.bilibili.com/video/BV1wf4y1s7TG?p=25
     */
    //和具名函数一样，匿名函数可以不带参数，也可以带一个或多个任何类型的参数，需要带参数时，参数的类型放在匿名函数的类型定义中，参数名则放在函数定义中
    val bf3:(String) -> String = {
//        name -> name + name.count()
        it + it.count()
    }
    val bf4:(str: String, age: Int) -> String = {
        str, age -> str + age
    }
    println("bf3 = ${bf3("Happy")}, bf4 = ${bf4("Birthday", 23)}")


    /**
     * 26-it关键字 https://www.bilibili.com/video/BV1wf4y1s7TG?p=26
     */
    //定义只有一个参数的匿名函数时，可以使用it关键字来表示参数名。当你需要传入两个值参，it关键字就不能用了


    /**
     * 27-匿名函数的类型推断
     */
    //定义一个变量时，如果已把匿名函数作为变量赋值给它，就不需要显示指明变量类型了。
    val bf5 = {
        val str = "World!"
        "Hello $str"
    }
    val bf6 = { name: String, age: Int ->
        "$name: $age"
    }
    println("bf5 = ${bf5()}, bf6 = ${bf6("张三", 23)}")
}

